// Copyright 2007-2009 Comdiv (F. Sadykov) - http://code.google.com/u/fagim.sadykov/
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// MODIFICATIONS HAVE BEEN MADE TO THIS FILE


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Web;
using Comdiv.Application;
using Comdiv.Inversion;
using Comdiv.IO;
using MvcContrib.Extensibility;


namespace MvcContrib.Comdiv.Framework{
    public class ExtensionsLoader{
        private readonly Dictionary<string, object> _defaultValues = new Dictionary<string, object>();
        private readonly Dictionary<string, object> _registry = new Dictionary<string, object>();
        private BooEval _boo;
        private volatile bool _inreloadMode;

        public ExtensionsLoader()
            : this((myapp.ioc.get<IFilePathResolver>() as ILayeredFilePathResolver).Layers.Select(x=>x.Replace("~/","")).ToArray()) {}

        public ExtensionsLoader(string[] roots) : this(roots, "extensions") {}

        public ExtensionsLoader(string[] roots,string dirname){
            Func<string, string> map = s =>{
                                           if (HttpContext.Current != null){
                                               return HttpContext.Current.Server.MapPath(s);
                                           }
                                           return s.Replace("~/", Environment.CurrentDirectory);
                                       }
                ;
            RootDirs =
                roots.Select(x => "~/" + x + (x == "" ? "" : "/") + dirname).Select(
                    x => map(x)).ToArray();
        }

        public string[] RootDirs { get; set; }

        public Dictionary<string, object> Registry{
            get { return _registry; }
        }

        public Dictionary<string, object> DefaultValues{
            get { return _defaultValues; }
        }

        public Assembly[] References { get; set; }


        public virtual T Get<T>(string name){
            if (Registry.ContainsKey(name)){
                return (T) Registry[name];
            }
            return default(T);
        }

        public virtual IList<T> Get<T>(){
            return Registry.Values.OfType<T>().ToList();
        }

        public virtual void Reload(){
            if (_inreloadMode){
                while (_inreloadMode){
                    Thread.Sleep(10);
                }
                return;
            }
            _inreloadMode = true;
            try{
                if (null == _boo){
                    _boo = new BooEval();
                }

                if (RootDirs.Length == 0){
                    return;
                }
                _boo.Interpreter.Reset();
                if (References != null){
                    foreach (Assembly assembly in References){
                        _boo.Interpreter.References.Add(assembly);
                    }
                }
                Registry.Clear();
                foreach (string rootDir in RootDirs){
                    if (!Directory.Exists(rootDir)){
                        continue;
                    }


                    _boo.Interpreter.SetValue("registry", Registry);
                    foreach (var pair in DefaultValues){
                        _boo.Interpreter.SetValue(pair.Key, pair.Value);
                    }
                    _boo.EvalDirectory(rootDir + "/imports");
                    _boo.EvalDirectory(rootDir);
                }
            }
            finally{
                _inreloadMode = false;
            }
        }

        private IInversionContainer _container;

        public IInversionContainer Container{
            get{
                if (_container.invalid()){
                    lock (this){
                        if (_container.invalid()){
                            Container = myapp.ioc;
                        }
                    }
                }
                return _container;
            }
            set { _container = value; }
        }

        public void Apply(){
            Reload();
            foreach (var pair in Registry){
                if(null==pair.Value){
                    continue;
                }
                if(pair.Value is Type){
                   Container.AddTransient(pair.Key, (Type) pair.Value);
                }else{
                    Container.AddSingleton(pair.Key, pair.Value.GetType(), pair.Value);
                }
               
            }
        }
    }
}